#ifndef __INSTANTANE_DU_PIANO_H
#define __INSTANTANE_DU_PIANO_H

#include "definitions.h"
#include <algorithm>
#include <string>
#include <iostream>
using std::ostream;

//
// Alternative statique
//
template <bool, class, class>
struct static_if_else;
template <class SiVrai, class SiFaux>
struct static_if_else<true, SiVrai, SiFaux>
{
	typedef SiVrai type;
};
template <class SiVrai, class SiFaux>
struct static_if_else<false, SiVrai, SiFaux>
{
	typedef SiFaux type;
};

//
// Comparaison statique de types
//
template <class T, class U>
struct static_same_type
{
	static const bool VAL = false;
};
template <class T>
struct static_same_type<T, T>
{
	static const bool VAL = true;
};

//
// Assertion statique
//
template <bool>
struct static_assert;
template <>
struct static_assert<true> {};

template <class T>
void unused(const T&)
{ }

class Illegal;

template <int NbBytes>
struct entier_nonsigne
{
	typedef typename
	static_if_else<
	sizeof(unsigned char)==NbBytes, unsigned char,
	typename static_if_else<
	sizeof(unsigned short)==NbBytes, unsigned short,
	typename static_if_else<
	sizeof(unsigned int)==NbBytes, unsigned int,
	typename static_if_else<
	sizeof(unsigned long)==NbBytes, unsigned long,
	Illegal
	>::type
	>::type
	>::type
	>::type type;
};

typedef entier_nonsigne<2>::type uint_16;
typedef entier_nonsigne<4>::type uint_32;

static static_assert <
!static_same_type<uint_16, Illegal>::VAL && !static_same_type<uint_32, Illegal>::VAL
> les_types_uint_16_et_uint_32_doivent_exister;

template <int n>
struct nb_uint_16_pour_ {
	static_assert<(n > 0)> n_doit_etre_strictement_positif;
	enum { touches = (n-1)/16 + 1 };
};

struct instantane_du_piano
{
	enum { nb_uint_16_touches = nb_uint_16_pour_<NB_TOUCHES_PIANO>::touches };
	enum { nombre_de_touches = NB_TOUCHES_PIANO };
	enum { taille_buffer = nb_uint_16_touches * sizeof(uint_16) };
	uint_32 temps;
	uint_16 touches[nb_uint_16_touches];
	
	instantane_du_piano() {
		unused(les_types_uint_16_et_uint_32_doivent_exister);
	}
	
	void laver() {
		std::fill(touches, touches + nb_uint_16_touches, 0);
	}
	
	bool touche_active(int x) {
		assert(x < nombre_de_touches);
		int q = nb_uint_16_touches - (x / 16) - 1;
		int r = x % 16;
		return ((touches[q] >> r) & 1) == 1;
	}
	
	void set_touche(int x, bool v) {
		assert(x < nombre_de_touches);
		int q = nb_uint_16_touches - (x / 16) - 1;
		int r = x % 16;
		uint_16 masque_v = 1 << r;
		if(v)
			touches[q] |= masque_v;
		else
			touches[q] &= std::numeric_limits<uint_16>::max() - masque_v;
	}
	
	touche_du_piano premiere_touche() {
		for(int i=0; i<nombre_de_touches; i++)
			if(touche_active(i))
				return static_cast<touche_du_piano>(i);
		return pas_de_touche;
	}
	
	std::string to_string() {
		std::string s("");
		for(int i=0; i<nb_uint_16_touches; i++)
			for(int j=0; j<16; j++) 
				s.push_back((uint_16)((touches[i] >> j) & 1));
		return s;
	}
	
	uint_16* begin() { return touches; }
	uint_16* end()   { return touches + nb_uint_16_touches; }
	
	friend ostream &operator<<(ostream &, instantane_du_piano&);
};

#endif